package com.zlc.workflow.flow;

import com.zlc.common.common.exception.WorkflowException;
import com.zlc.common.core.process.model.vo.StoneProcessVo;
import com.zlc.common.core.process.service.StoneProcessService;
import com.zlc.workflow.flow.node.ProcessModel;
import com.zlc.workflow.flow.node.TaskNode;
import com.zlc.workflow.flow.parser.ProcessBuilder;
import com.zlc.workflow.flow.parser.ProcessBuilderFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Component
public class NodeUtils {

    private static StoneProcessService stoneProcessService;
    private static ExpressionParser expressionParser;

    @Autowired
    public void setStoneProcessService(StoneProcessService stoneProcessService){
        NodeUtils.stoneProcessService = stoneProcessService;
    }
    @Autowired
    public void setExpressionParser(ExpressionParser expressionParser){
        NodeUtils.expressionParser = expressionParser;
    }

    public static TaskNode getFirstTask(String content,String processId){
        ProcessModel processModel = processModel(content,processId);
        return processModel.getFirstTaskNode();
    }

    public static String getProcessContent(byte[] bytes){
        if (!ObjectUtils.isEmpty(bytes)) {
            return new String(bytes, StandardCharsets.UTF_8);
        }
        return null;
    }

    public static ProcessModel processModel(String content,String processId){
        ProcessBuilder builder = ProcessBuilderFactory.getBuilder(content);
        builder.buildProcess(processId);
        return builder.getProcessModel();
    }

    public static TaskNode getTaskNode(String content,String processId,String nodeId){
        ProcessModel processModel = processModel(content,processId);
        List<NodeModel> allTaskNodes = processModel.getAllTaskNodes();
        if(!ObjectUtils.isEmpty(allTaskNodes)){
            return (TaskNode) allTaskNodes.stream().filter(a->a.nodeId().equals(nodeId))
                    .findFirst().orElse(null);
        }
        throw new WorkflowException("节点不存在！->"+nodeId);
    }

    public static TaskNode getFirstTask(String processId){
        StoneProcessVo processVo = stoneProcessService.getByProcessId(processId);
        if(ObjectUtils.isEmpty(processVo)){
            return null;
        }
        return getFirstTask(getProcessContent(processVo.getContent()),processId);
    }

    public static TaskNode getTaskNode(String processId,String nodeId){
        StoneProcessVo processVo = stoneProcessService.getByProcessId(processId);
        if(ObjectUtils.isEmpty(processVo)){
            return null;
        }
        return getTaskNode(getProcessContent(processVo.getContent()),processId,nodeId);
    }

    public static List<NodeModel> getAllTaskNode(String processId){
        StoneProcessVo processVo = stoneProcessService.getByProcessId(processId);
        if(ObjectUtils.isEmpty(processVo)){
            return null;
        }
        String processContent = getProcessContent(processVo.getContent());
        return processModel(processContent,processId).getAllTaskNodes();
    }

    public static List<NodeModel> getByNodeName(String content,String processId,String nodeName){
        ProcessModel processModel = processModel(content,processId);
        List<NodeModel> allNodes = processModel.getAllNodes();
        if(!ObjectUtils.isEmpty(allNodes)){
            return allNodes.stream().filter(a->a.nodeName().equals(nodeName))
                    .collect(Collectors.toList());
        }
        throw new WorkflowException("节点不存在！->"+nodeName);
    }

    /**
     * 获取节点前面的所有任务节点
     */
    public static List<NodeModel> getAllPreTaskNode(NodeModel nodeModel){
        List<NodeModel> nodeModels = nodeModel.preTask();
        List<NodeModel> taskModels = new ArrayList<>();
        for(NodeModel node : nodeModels){
            taskModels.add(node);
            loadAllPreTask(taskModels,node);
        }
        return distinct(taskModels);
    }

    private static List<NodeModel> distinct(List<NodeModel> nodeModels){
        if(ObjectUtils.isEmpty(nodeModels)){
            return new ArrayList<>();
        }
        List<String> nodeIds = new ArrayList<>();
        List<NodeModel> distincts = new ArrayList<>();
        for(NodeModel nodeModel : nodeModels){
            if(!nodeIds.contains(nodeModel.nodeId())){
                distincts.add(nodeModel);
            }
            nodeIds.add(nodeModel.nodeId());
        }
        return distincts;
    }


    private static void loadAllPreTask(List<NodeModel> taskNodes,NodeModel preTask){
        List<NodeModel> preNodeModels = preTask.preTask();
        if(!ObjectUtils.isEmpty(preNodeModels)){
            for(NodeModel preNode : preNodeModels){
                taskNodes.add(preNode);
                loadAllPreTask(taskNodes,preNode);
            }
        }
    }

    public static String transferVariable(Map<String,Object> param,String config){
        for(Map.Entry<String,Object> map :param.entrySet()){
            String key = map.getKey();
            Object value = map.getValue();
            value = value == null ? "" : value;
            config = config.replaceAll("\\$\\{"+key+"}",String.valueOf(value));
        }
        return config;
    }

    public static Boolean expressBool(Map<String,Object> param,String express){
        String expressV = transferVariable(param,express);
        Expression expression = expressionParser.parseExpression(expressV);
        return expression.getValue(Boolean.class);
    }

}
